package com.lyc.common.interceptor;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.lyc.common.constants.CommonConstants;
import com.lyc.common.constants.DepartCommonConstant;
import com.lyc.common.holder.UserContextHolder;
import com.lyc.common.util.ReflectUtil;
import lombok.SneakyThrows;

/**
 * @Author 李大神仙
 * @Date 2024/3/4 20:06
 * @Version 1.0
 */
public class MybatisMetaObjectInterceptor extends JsqlParserSupport implements InnerInterceptor{
    @SneakyThrows
    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms,Object parameter)throws SQLException{
        Object formParameter = getEntityFromParameter(parameter);
        if (Objects.isNull(formParameter)){
            InnerInterceptor.super.beforeUpdate(executor,ms,parameter);
        }else {
            try{
                setParameter(formParameter,ms.getSqlCommandType());
            }catch (Exception e){
                InnerInterceptor.super.beforeUpdate(executor,ms,parameter);
            }
        }
    }

    private Object getEntityFromParameter(Object parameter){
        if (parameter instanceof Map){
            Map map = (Map)parameter;
            if (map.containsKey("et")){
                return map.get("et");
            }
            return null;
        }else if (parameter != null){
            return parameter;
        }
        throw new MybatisPlusException("Failed to process,the parameter object is null.");
    }

    public void setParameter(Object parameter, SqlCommandType sqlCommandType)throws Throwable{
        //如果常用字段提取了公共类 BaseEntity
        //获取私有成员变量
        if (SqlCommandType.INSERT.equals(sqlCommandType)){//insert 语句插入createBy
            // 获取字段
            setByUser(DepartCommonConstant.DbCommonColumn.ADD_USER,parameter);
            setDateField(DepartCommonConstant.DbCommonColumn.ADD_TIME,parameter);
            setByUser(DepartCommonConstant.DbCommonColumn.UPD_USER,parameter);
            setDateField(DepartCommonConstant.DbCommonColumn.UPD_TIME,parameter);
            setByUser(DepartCommonConstant.DbCommonColumn.SYSTEM_CODE,parameter);
            setByUser(DepartCommonConstant.DbCommonColumn.TENANT_ID,parameter);
        }

        if (SqlCommandType.UPDATE.equals(sqlCommandType)){
            setByUser(DepartCommonConstant.DbCommonColumn.UPD_USER,parameter);
            setDateField(DepartCommonConstant.DbCommonColumn.UPD_TIME,parameter);
        }
    }

    public void setByUser(String fieldName,Object object){
        if (ReflectUtil.hasField(object.getClass(),fieldName)){
            try {
                //获取字段，从当前类中查找
                Class<?> clazz = object.getClass();
                Field declaredField = null;
                try {
                    declaredField = clazz.getDeclaredField(fieldName);
                }catch (NoSuchFieldException e){
                }
                if (null != declaredField){
                    UserContextHolder contextHolder = UserContextHolder.getInstance();
                    declaredField.setAccessible(true);
                    if (DepartCommonConstant.DbCommonColumn.ADD_USER.equals(fieldName) ||
                    DepartCommonConstant.DbCommonColumn.UPD_USER.equals(fieldName)){
                        declaredField.set(object,Objects.isNull(contextHolder) ?
                                CommonConstants.SYSTEM_USER : contextHolder.getUserId());
                    }else if (DepartCommonConstant.DbCommonColumn.SYSTEM_CODE.equals(fieldName)){
                        declaredField.set(object,Objects.isNull(contextHolder) ?
                                CommonConstants.SYSTEM_CODE : contextHolder.getAppCode());
                    }else if (DepartCommonConstant.DbCommonColumn.TENANT_ID.equals(fieldName)){
                        declaredField.set(object,Objects.isNull(contextHolder) ?
                                CommonConstants.SYSTEM_TENANT_ID : contextHolder.getTenantId());
                    }
                    declaredField.setAccessible(false);
                    return;
                }
                findSupper(fieldName,object,clazz);
            }catch (IllegalAccessException e){
                throw new RuntimeException(e);
            }
        }
    }

    private static void findSupper(String fieldName,Object object,Class<?> clazz)throws  IllegalAccessException{
        //从父类中查找 -- 仅限public方法
        Field field = null;
        Class<?> superclass = null;
        try {
            superclass = clazz.getSuperclass();
            if (superclass == null){
                return;
            }
            field = superclass.getDeclaredField(fieldName);
        }catch (NoSuchFieldException e){
        }

        if (null != field){
            UserContextHolder contextHolder = UserContextHolder.getInstance();
            field.setAccessible(true);
            if (DepartCommonConstant.DbCommonColumn.ADD_USER.equals(fieldName) ||
            DepartCommonConstant.DbCommonColumn.UPD_USER.equals(fieldName)){
                field.set(object,Objects.isNull(contextHolder) ? CommonConstants.SYSTEM_USER :
                        contextHolder.getUserId());
            }else if (DepartCommonConstant.DbCommonColumn.SYSTEM_CODE.equals(fieldName)){
                field.set(object,Objects.isNull(contextHolder) ? CommonConstants.SYSTEM_CODE :
                        contextHolder.getAppCode());
            }else if (DepartCommonConstant.DbCommonColumn.TENANT_ID.equals(fieldName)){
                field.set(object,Objects.isNull(contextHolder) ? CommonConstants.SYSTEM_TENANT_ID :
                        contextHolder.getTenantId());
            }
            field.setAccessible(false);
            return;
        }
        findSupper(fieldName,object,superclass);
    }

    public void setUserName(String fieldName,Object object){
        if (ReflectUtil.hasField(object.getClass(),fieldName)){
            try {
                //获取字段，从当前类中查找
                Class<?> clazz = object.getClass();
                Field declaredField = null;
                try {
                    declaredField = clazz.getDeclaredField(fieldName);
                }catch (NoSuchFieldException e){
                }
                String userName = UserContextHolder.getInstance().getUserName();
                if (null != declaredField){
                    declaredField.setAccessible(true);
                    declaredField.set(object, StringUtils.isEmpty(userName) ?
                            CommonConstants.SYSTEM_USER : userName);
                    declaredField.setAccessible(false);
                    return;
                }
                //从父类中查找 仅限public方法
                Field field = null;
                try {
                    Class<?> superclass = clazz.getSuperclass();
                    field = superclass.getDeclaredField(fieldName);
                }catch (NoSuchFieldException e){
                }
                if (null != field){
                    field.setAccessible(true);
                    field.set(object,StringUtils.isEmpty(userName) ? CommonConstants.SYSTEM_USER : userName);
                    field.setAccessible(false);
                }
            }catch (IllegalAccessException e){
                throw new RuntimeException(e);
            }
        }
    }

    public void setDateField(String fieldName,Object object){
        if (ReflectUtil.hasField(object.getClass(),fieldName)){
            try {
                //获取字段，从当前类中查找
                Class<?> clazz = object.getClass();
                Field declaredField = null;
                try {
                    declaredField = clazz.getDeclaredField(fieldName);
                }catch (NoSuchFieldException e){
                }
                if (null != declaredField){
                    extracted(object,declaredField);
                    return;
                }
                findDateSupper(fieldName,object,clazz);
            }catch (IllegalAccessException e){
                throw new RuntimeException(e);
            }
        }
    }

    private static void findDateSupper(String fieldName,Object object,Class<?> clazz)throws IllegalAccessException{
        //从父类中查找 仅限public方法
        Field field = null;
        Class<?> superclass = null;
        try {
            superclass = clazz.getSuperclass();
            if (superclass == null){
                return;
            }
            field = superclass.getDeclaredField(fieldName);
        }catch (NoSuchFieldException e){
        }
        if (null != field){
            extracted(object,field);
            return;
        }
        findDateSupper(fieldName,object,superclass);
    }

    private static void extracted(Object object,Field declaredField)throws IllegalAccessException{
        declaredField.setAccessible(true);
        Class<?> type = declaredField.getType();
        if (type == Date.class){
            declaredField.set(object,new Date());
        }
        if (type == LocalDateTime.class){
            declaredField.set(object,LocalDateTime.now());
        }
        if (type == LocalDate.class){
            declaredField.set(object,LocalDate.now());
        }
        if (type == String.class){
            //定义日期格式
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String formattedDate = LocalDateTime.now().format(formatter);
            declaredField.set(object,formattedDate);
        }
        if (type == Timestamp.class){
            declaredField.set(object,new Timestamp(System.currentTimeMillis()));
        }
        declaredField.setAccessible(Boolean.FALSE);
    }
}
































